package hasher

import (
	"encoding/binary"
	"encoding/json"
	"hash"
	"hash/fnv"
	"reflect"
	"time"

	"gitee.com/gousing/helper/internal"
)

const (
	hashFnvOffset32 uint32 = 2166136261
	hashFnvPrime32  uint32 = 16777619
)

// Fnv32Any 计算任意类型的Fnv32哈希值, 无法处理时返回 hashFnvOffset32
func Fnv32Any(v any) uint32 {
	if v == nil {
		return hashFnvOffset32
	}
	h := fnv.New32a()
	fav32AnyType(h, v)
	if h.Size() == 0 {
		return hashFnvOffset32
	}
	return h.Sum32()
}

func Fnv32String(v string) uint32 {
	if v == "" {
		return hashFnvOffset32
	}
	hash := hashFnvOffset32
	for i := 0; i < len(v); i++ {
		hash ^= uint32(v[i])
		hash *= hashFnvPrime32
	}
	return hash
}
func Fnv32Bytes(v []byte) uint32 {
	if len(v) == 0 {
		return hashFnvOffset32
	}
	h := fnv.New32a()
	fnv32Bytes(h, v)
	return h.Sum32()
}
func Fnv32Int(v int) uint32 {
	h := fnv.New32a()
	fnv32Int(h, v)
	return h.Sum32()
}
func Fnv32Int64(v int64) uint32 {
	h := fnv.New32a()
	fnv32Int64(h, v)
	return h.Sum32()
}
func Fnv32Int32(v int32) uint32 {
	h := fnv.New32a()
	fnv32Int32(h, v)
	return h.Sum32()
}
func Fnv32Int16(v int16) uint32 {
	h := fnv.New32a()
	fnv32Int16(h, v)
	return h.Sum32()
}
func Fnv32Int8(v int8) uint32 {
	h := fnv.New32a()
	fnv32Int8(h, v)
	return h.Sum32()
}
func Fnv32Uint(v uint) uint32 {
	h := fnv.New32a()
	fnv32Uint(h, v)
	return h.Sum32()
}
func Fnv32Uint64(v uint64) uint32 {
	h := fnv.New32a()
	fnv32Uint64(h, v)
	return h.Sum32()
}
func Fnv32Uint32(v uint32) uint32 {
	h := fnv.New32a()
	fnv32Uint32(h, v)
	return h.Sum32()
}
func Fnv32Uint16(v uint16) uint32 {
	h := fnv.New32a()
	fnv32Uint16(h, v)
	return h.Sum32()
}
func Fnv32Uint8(v uint8) uint32 {
	h := fnv.New32a()
	fnv32Uint8(h, v)
	return h.Sum32()
}
func Fnv32Float32(v float32) uint32 {
	h := fnv.New32a()
	fnv32Float32(h, v)
	return h.Sum32()
}
func Fnv32Float64(v float64) uint32 {
	h := fnv.New32a()
	fnv32Float64(h, v)
	return h.Sum32()
}
func Fnv32Bool(v bool) uint32 {
	h := fnv.New32a()
	fnv32Bool(h, v)
	return h.Sum32()
}
func Fnv32Complex64(v complex64) uint32 {
	var z complex64
	if v == z {
		return hashFnvOffset32
	}
	h := fnv.New32a()
	fnv32Complex64(h, v)
	return h.Sum32()
}
func Fnv32Complex128(v complex128) uint32 {
	var z complex128
	if v == z {
		return hashFnvOffset32
	}
	h := fnv.New32a()
	fnv32Complex128(h, v)
	return h.Sum32()
}
func Fnv32Time(t time.Time) uint32 {
	var z time.Time
	if t == z || t.IsZero() {
		return hashFnvOffset32
	}
	h := fnv.New32a()
	fnv32Int64(h, t.UnixNano())
	return h.Sum32()
}
func Fnv32Duration(t time.Duration) uint32 {
	if t == 0 {
		return hashFnvOffset32
	}
	h := fnv.New32a()
	fnv32Int64(h, t.Nanoseconds())
	return h.Sum32()
}
func Fnv32Uintptr(v uintptr) uint32 {
	if v == 0 {
		return hashFnvOffset32
	}
	h := fnv.New32a()
	fnv32Uintptr(h, v)
	return h.Sum32()
}

func fav32AnyType(h hash.Hash32, v any) {
	if v == nil {
		// nil pointer
		return
	}
	switch val := v.(type) {
	case string:
		if len(val) == 0 {
			return
		}
		fnv32String(h, val)
	case []byte:
		if len(val) == 0 {
			return
		}
		fnv32Bytes(h, val)
	case bool:
		fnv32Bool(h, val)
	case int:
		fnv32Int(h, val)
	case int64:
		fnv32Int64(h, val)
	case int32: //Same Rune
		fnv32Int32(h, val)
	case int16:
		fnv32Int16(h, val)
	case int8:
		fnv32Int8(h, val)
	case uint:
		fnv32Uint(h, val)
	case uint64:
		fnv32Uint64(h, val)
	case uint32:
		fnv32Uint32(h, val)
	case uint16:
		fnv32Uint16(h, val)
	case uint8:
		fnv32Uint8(h, val)
	case float32:
		fnv32Float32(h, val)
	case float64:
		fnv32Float64(h, val)
	case complex64:
		if val == 0 {
			return
		}
		fnv32Complex64(h, val)
	case complex128:
		if val == 0 {
			return
		}
		fnv32Complex128(h, val)
	case time.Time:
		if val.IsZero() {
			return
		}
		fnv32Time(h, val)
	case time.Duration:
		if val == 0 {
			return
		}
		fnv32Duration(h, val)
	case uintptr:
		if val == 0 {
			return
		}
		fnv32Uintptr(h, val)
	case error:
		s := val.Error()
		if s == "" {
			return
		}
		fnv32String(h, val.Error())
	case *string:
		if len(*val) == 0 {
			return
		}
		fnv32String(h, *val)
	case *[]byte:
		if len(*val) == 0 {
			return
		}
		fnv32Bytes(h, *val)
	case *bool:
		fnv32Bool(h, *val)
	case *int:
		fnv32Int(h, *val)
	case *int64:
		fnv32Int64(h, *val)
	case *int32: //Same Rune
		fnv32Int32(h, *val)
	case *int16:
		fnv32Int16(h, *val)
	case *int8:
		fnv32Int8(h, *val)
	case *uint:
		fnv32Uint(h, *val)
	case *uint64:
		fnv32Uint64(h, *val)
	case *uint32:
		fnv32Uint32(h, *val)
	case *uint16:
		fnv32Uint16(h, *val)
	case *uint8:
		fnv32Uint8(h, *val)
	case *float32:
		fnv32Float32(h, *val)
	case *float64:
		fnv32Float64(h, *val)
	case *complex64:
		if *val == 0 {
			return
		}
		fnv32Complex64(h, *val)
	case *complex128:
		if *val == 0 {
			return
		}
		fnv32Complex128(h, *val)
	case *time.Time:
		if (*val).IsZero() {
			return
		}
		fnv32Time(h, *val)
	case *time.Duration:
		if *val == 0 {
			return
		}
		fnv32Duration(h, *val)
	case *uintptr:
		if *val == 0 {
			return
		}
		fnv32Uintptr(h, *val)
	case *error:
		s := (*val).Error()
		if s == "" {
			return
		}
		fnv32String(h, s)
	case struct{}, *struct{}:
		fnv32String(h, "struct{}")
	default:
		if b, ok := v.(interface{ Bytes() []byte }); ok {
			s := b.Bytes()
			if len(s) == 0 {
				return
			}
			_, _ = h.Write(s)
			return
		}
		if b, ok := v.(interface{ String() string }); ok {
			s := b.String()
			if s == "" {
				return
			}
			_, _ = h.Write(internal.StringToBytes(s))
			return
		}
		refVal := reflect.ValueOf(v)
		if !refVal.IsValid() {
			// IsValid reports whether v represents a value.
			// It returns false if v is the zero Value.
			// If [Value.IsValid] returns false, all other methods except String panic.
			// Most functions and methods never return an invalid Value.
			// If one does, its documentation states the conditions explicitly.
			return
		}
		for refVal.Kind() == reflect.Ptr {
			refVal = refVal.Elem()
			if !refVal.IsValid() {
				// 如果指针指向的值无效，直接返回
				return
			}
		}
		switch refVal.Kind() {
		case reflect.Chan:
			ptr := refVal.Pointer()
			var bytes [8]byte
			for i := range 8 {
				bytes[i] = byte(ptr >> (uint(i) * 8))
			}
			_, _ = h.Write(bytes[:])
			return
		case reflect.String,
			reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
			reflect.Uintptr,
			reflect.Float32, reflect.Float64,
			reflect.Complex64, reflect.Complex128,
			reflect.Bool:
			fav32AnyType(h, refVal.Interface())
			return
		}
		if bytes, err := json.Marshal(refVal.Interface()); err == nil {
			_, _ = h.Write(bytes)
			return
		}
		// 法处理时返回 hashFnvOffset32
		return
	}
}
func fnv32String(h hash.Hash32, s string) {
	_, _ = h.Write(internal.StringToBytes(s))
}

func fnv32Bytes(h hash.Hash32, b []byte) {
	_, _ = h.Write(b)
}

func fnv32Bool(h hash.Hash32, b bool) {
	if b {
		_, _ = h.Write([]byte{1})
	} else {
		_, _ = h.Write([]byte{0})
	}
}
func fnv32Int(h hash.Hash32, n int) {
	if internal.Is64Bit {
		binary.Write(h, binary.BigEndian, int64(n))
	} else {
		binary.Write(h, binary.BigEndian, int32(n))
	}
}
func fnv32Int8(h hash.Hash32, n int8) {
	binary.Write(h, binary.BigEndian, &n)
}
func fnv32Int16(h hash.Hash32, n int16) {
	binary.Write(h, binary.BigEndian, &n)
}
func fnv32Int32(h hash.Hash32, n int32) {
	binary.Write(h, binary.BigEndian, &n)
}
func fnv32Int64(h hash.Hash32, n int64) {
	binary.Write(h, binary.BigEndian, &n)
}

func fnv32Float32(h hash.Hash32, n float32) {
	binary.Write(h, binary.BigEndian, &n)
}

func fnv32Float64(h hash.Hash32, n float64) {
	binary.Write(h, binary.BigEndian, &n)
}

func fnv32Uint(h hash.Hash32, n uint) {
	if internal.Is64Bit {
		n64 := uint64(n)
		_, _ = h.Write([]byte{byte(n64 >> 56), byte(n64 >> 48), byte(n64 >> 40), byte(n64 >> 32), byte(n64 >> 24), byte(n64 >> 16), byte(n64 >> 8), byte(n)})
	} else {
		n32 := uint32(n)
		_, _ = h.Write([]byte{byte(n32 >> 24), byte(n32 >> 16), byte(n32 >> 8), byte(n32)})
	}
}
func fnv32Uint8(h hash.Hash32, n uint8) {
	_, _ = h.Write([]byte{byte(n)})
}
func fnv32Uint16(h hash.Hash32, n uint16) {
	_, _ = h.Write([]byte{byte(n >> 8), byte(n)})
}
func fnv32Uint32(h hash.Hash32, n uint32) {
	_, _ = h.Write([]byte{byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)})
}

func fnv32Uint64(h hash.Hash32, n uint64) {
	_, _ = h.Write([]byte{byte(n >> 56), byte(n >> 48), byte(n >> 40), byte(n >> 32), byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)})
}
func fnv32Complex64(h hash.Hash32, n complex64) {
	binary.Write(h, binary.LittleEndian, real(n))
	binary.Write(h, binary.LittleEndian, imag(n))
}

func fnv32Complex128(h hash.Hash32, n complex128) {
	binary.Write(h, binary.LittleEndian, real(n))
	binary.Write(h, binary.LittleEndian, imag(n))
}
func fnv32Time(h hash.Hash32, t time.Time) {
	fnv32Int64(h, t.UnixNano())
}
func fnv32Duration(h hash.Hash32, t time.Duration) {
	fnv32Int64(h, t.Nanoseconds())
}
func fnv32Uintptr(h hash.Hash32, n uintptr) {
	if internal.Is64Bit {
		u64 := uint64(n)
		_, _ = h.Write([]byte{byte(u64 >> 56), byte(u64 >> 48), byte(u64 >> 40), byte(u64 >> 32), byte(u64 >> 24), byte(u64 >> 16), byte(u64 >> 8), byte(u64)})
	} else {
		u32 := uint32(n)
		_, _ = h.Write([]byte{byte(u32 >> 24), byte(u32 >> 16), byte(u32 >> 8), byte(u32)})
	}
}
